﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using Pavilion.DesignerTool.ViewTools.ConnectableListBox;
using Pavilion.DesignerTool.ViewTools;

namespace Pavilion.DesignerTool
{
    /// <summary>
    /// Interaction logic for GameToolDataView.xaml
    /// </summary>
    public partial class GameDataEditorView : UserControl
    {
        public GameDataEditorViewModel ViewModel { get; set; }

        public GameDataEditorView()
        {
            InitializeComponent();

            if (!DesignerProperties.GetIsInDesignMode(this))
                propertyGrid.DataContext = DataContext = ViewModel = new GameDataEditorViewModel();
        }

        public void LoadResource(AnimationResourceData resourceData)
        {
            ViewModel.LoadResource(resourceData);
        }

        public void SetGameData(NodeBaseWrapper nodeBaseWrapper)
        {
            ViewModel.NodeBaseWrapper = nodeBaseWrapper;
            
            if (ViewModel.NodeBaseWrapper != null && ViewModel.NodeBaseWrapper.Node is AnimationNode)
                animationPanel.Visibility = Visibility.Visible;
            else
                animationPanel.Visibility = Visibility.Collapsed;
        }

        private void AddGameDataMenuItem_Click(object sender, RoutedEventArgs e)
        {
            if ((e.OriginalSource as FrameworkElement).DataContext is Type)
            {
                IGameDataInfo gameDataInfo = Activator.CreateInstance((e.OriginalSource as FrameworkElement).DataContext as Type) as IGameDataInfo;
                ViewModel.AddGameData(new GameDataWrapper(new GameData(gameDataInfo)));
            }
        }

        private void RemoveGameDataMenuItem_Click(object sender, RoutedEventArgs e)
        {
            ViewModel.RemoveGameDataCategory((sender as FrameworkElement).DataContext as PropertyCategory);
        }

        private void AddPropertyMenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (e.OriginalSource == sender)
                return;

            MenuItem menuItem = e.OriginalSource as MenuItem;
            PropertyCategory propertyCategory = (sender as FrameworkElement).DataContext as PropertyCategory;
            (propertyCategory.AssociatedItem as GameDataWrapper).AddProperty((string)menuItem.Header);
            ViewModel.UpdateGameDataProperties();
        }

        private void RemovePropertyMenuItem_Click(object sender, RoutedEventArgs e)
        {
            PropertyItemWrapper itemWrapper = (sender as FrameworkElement).DataContext as PropertyItemWrapper;
            (itemWrapper.PropertyItem.AssociatedItem as GameDataWrapper).RemoveProperty(itemWrapper.PropertyName);
            ViewModel.UpdateGameDataProperties();
        }
    }

    public class GameDataEditorViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public NodeBaseWrapper NodeBaseWrapper { get { return nodeBaseWrapper; } set { SetNodeBaseWrapper(value); } }
        public NotifiableCollection<Type> GameDataTypeCollection { get; private set; }
        public NotifiableCollection<PropertyCategory> Properties { get; private set; }
        public NotifiableCollection<AnimationData> AnimationCollection { get { return animationCollection; } set { animationCollection = value; OnPropertyChanged("AnimationCollection"); } }

        private NodeBaseWrapper nodeBaseWrapper;
        private NotifiableCollection<AnimationData> animationCollection;

        public GameDataEditorViewModel()
        {
            GameDataTypeCollection = new NotifiableCollection<Type>(GameEngine.ObtainAllTypes<IGameDataInfo>("Pavilion.GameTools.dll"));
            Properties = new NotifiableCollection<PropertyCategory>();
        }

        public void LoadResource(AnimationResourceData resourceData)
        {
            AnimationCollection = resourceData.AnimationDataCollection;
        }

        public void UpdateGameDataProperties()
        {
            ClearProperties();

            if (nodeBaseWrapper == null)
                return;

            foreach (GameDataWrapper gameDataWrapper in NodeBaseWrapper.GameDataWrapperCollection)
                LoadPropertiesForGameData(gameDataWrapper);
        }

        public void AddGameData(GameDataWrapper gameDataWrapper)
        {
            nodeBaseWrapper.AddGameData(gameDataWrapper);

            LoadPropertiesForGameData(gameDataWrapper);
        }

        public void RemoveGameDataCategory(PropertyCategory category)
        {
            GameDataWrapper gameDataWrapper = category.AssociatedItem as GameDataWrapper;
            nodeBaseWrapper.RemoveGameData(gameDataWrapper);
            Properties.Remove(category);
            GameDataTypeCollection.Add(gameDataWrapper.GameData.DataInfo.GetType());
        }

        private void LoadPropertiesForGameData(GameDataWrapper gameDataWrapper)
        {
            PropertyCategory propertyCategory = new PropertyCategory(gameDataWrapper);

            foreach (string propertyName in gameDataWrapper.PropertiesCollection)
                propertyCategory.AddProperty(new GameDataPropertyItem(gameDataWrapper, propertyName));

            GameDataTypeCollection.Remove(gameDataWrapper.GameData.DataInfo.GetType());
            Properties.Add(propertyCategory);
        }

        private void ClearProperties()
        {
            while (Properties.Count != 0)
            {
                GameDataTypeCollection.Add((Properties[0].AssociatedItem as GameDataWrapper).GameData.DataInfo.GetType());
                Properties.RemoveAt(0);
            }
        }

        private void SetNodeBaseWrapper(NodeBaseWrapper value)
        {
            nodeBaseWrapper = value;

            UpdateGameDataProperties();

            OnPropertyChanged("NodeBaseWrapper");
        }

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class GameDataPropertyItem : IPropertyItem
    {
        public string PropertyName { get; private set; }
        public object PropertyValue { get { return gameDataWrapper.GameData.PropertyDictionary[PropertyName]; } set { gameDataWrapper.GameData.PropertyDictionary[PropertyName] = value; } }
        public object AssociatedItem { get { return gameDataWrapper; } }

        private GameDataWrapper gameDataWrapper;

        public GameDataPropertyItem(GameDataWrapper gameData, string key)
        {
            this.gameDataWrapper = gameData;
            this.PropertyName = key;
        }
    }

    public class GameDataWrapper
    {
        public GameData GameData { get; private set; }
        public NotifiableCollection<string> PropertiesCollection { get; private set; }
        public NotifiableCollection<string> CompleteListOfProperties { get; private set; }

        public GameDataWrapper(GameData gameData)
        {
            this.GameData = gameData;

            PropertiesCollection = new NotifiableCollection<string>(gameData.PropertyDictionary.Keys);
            CompleteListOfProperties = new NotifiableCollection<string>(gameData.DataInfo.CompletePropertyList);

            foreach (string propertyName in gameData.PropertyDictionary.Keys)
                CompleteListOfProperties.Remove(propertyName);
        }

        public void AddProperty(string propertyName)
        {
            CompleteListOfProperties.Remove(propertyName);
            GameData.AddData(propertyName, GameData.DataInfo.ObtainPropertyInfo(propertyName).DefaultValue);
            PropertiesCollection.Add(propertyName);
        }

        public void RemoveProperty(string propertyName)
        {
            CompleteListOfProperties.Add(propertyName);
            GameData.PropertyDictionary.Remove(propertyName);
            PropertiesCollection.Remove(propertyName);
        }

        public override string ToString()
        {
            return GameData.DataInfo.GetType().Name;
        }
    }
}
